#![allow(clippy::upper_case_acronyms)]
#![allow(non_camel_case_types)]

pub mod algebraic_expr;
pub mod bool_expr;
pub mod declear;
pub mod function;
pub mod general;
pub mod keyword;
pub mod literal;
pub mod operator;
pub mod statement;

use from_pest::FromPest;
use pest::Parser as _;
use pest::Span;
use pest::error::LineColLocation;
use pest_ast::FromPest;
use pest_derive::Parser;
use tracing::debug;
use tracing::trace;

use crate::ast_define::general::CompUnit;

#[derive(Parser)]
#[grammar = "parser.pest"]
pub struct SysYParser;

#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::program))]
pub struct Program {
    pub comp_units: Vec<CompUnit>,
}

impl std::fmt::Display for Program {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        enum Above {
            FileStart,
            Decl,
            FuncDef,
        }
        let mut above = Above::FileStart;
        for comp_unit in self.comp_units.iter() {
            match comp_unit {
                CompUnit::Decl(decl) => {
                    match above {
                        Above::FileStart | Above::Decl => Ok(()),
                        Above::FuncDef => writeln!(f),
                    }?;
                    write!(f, "{decl}")?;
                    above = Above::Decl;
                }
                CompUnit::FuncDef(func_def) => {
                    match above {
                        Above::FileStart => Ok(()),
                        Above::Decl | Above::FuncDef => writeln!(f),
                    }?;
                    write!(f, "{func_def}")?;
                    above = Above::FuncDef;
                }
            }
            #[allow(clippy::unwrap_used, reason = "能进循环则列表不为空")]
            if comp_unit != self.comp_units.last().unwrap() {
                writeln!(f)?;
            }
        }
        Ok(())
    }
}

#[derive(Debug, PartialEq, Clone)]
pub struct MetaInfo {
    pub line: usize,
    pub column: usize,
    pub start: usize,
    pub end: usize,
}

pub fn get_meta_info(span: Span) -> MetaInfo {
    let (line, column) = span.start_pos().line_col();
    MetaInfo {
        line,
        column,
        start: span.start(),
        end: span.end(),
    }
}

#[tracing::instrument(skip_all)]
pub fn try_parse(input: &str) -> Program {
    pest::set_error_detail(true);
    let row_parser = match SysYParser::parse(Rule::program, input) {
        Ok(row_parser) => row_parser,
        Err(parse_error) => {
            trace!("错误信息: \n{:#?}", parse_error);
            let error_line = match parse_error.line_col {
                LineColLocation::Pos((line, _)) => line,
                LineColLocation::Span((start_line, _), ..) => start_line,
            };
            panic!("Error type B at Line {error_line}:");
        }
    };
    trace!("\nrow_parser:\n{row_parser:#?}");
    match Program::from_pest(&mut row_parser.clone()) {
        Ok(program) => {
            // crate::write_to_file_for_log(&program, "program");
            program
        }
        Err(error) => panic!("\nerror:\n{error:#?}"),
    }
}
